use crate::config::db_config;
use crate::error::{Error, Result};
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use tracing::{error, info};

// sql files
const SQL_RECREATE_DB_FILE_NAME: &str = "00-recreate-db.sql";
const SQL_DIR: &str = "sql/dev_initial";

pub async fn recreate_db() -> Result<()> {
    let config = db_config();

    info!("Doing recreate database");

    // -- Get the sql_dir
    let _ = init_db().await;
    let sql_dir = get_sql_dir();

    // -- Get sql files.
    let mut paths: Vec<PathBuf> = fs::read_dir(sql_dir)
        .map_err(|_| Error::IO)?
        .filter_map(|entry| entry.ok().map(|e| e.path()))
        .collect();
    paths.sort();

    // -- SQL Execute each file.
    for path in paths {
        let path_str = path.to_string_lossy();

        if path_str.ends_with(".sql") && !path_str.ends_with(SQL_RECREATE_DB_FILE_NAME) {
            pexec(&path, &config.PG_APP_URL).await?;
        }
    }
    info!("Done recreate database");
    Ok(())
}

async fn init_db() -> Result<()> {
    let config = db_config();
    let sql_dir = get_sql_dir();
    // -- Create the app_db/app_user with the postgres user.
    {
        let sql_recreate_db_file = sql_dir.join(SQL_RECREATE_DB_FILE_NAME);
        pexec(&sql_recreate_db_file, &config.PG_POSTGRES_URL).await?;
    }
    Ok(())
}

fn get_sql_dir() -> PathBuf {
    let current_dir = std::env::current_dir().unwrap();
    let v: Vec<_> = current_dir.components().collect();
    let path_comp: Option<&std::path::Component<'_>> = v.get(v.len().wrapping_sub(3));
    let base_dir = if Some(true) == path_comp.map(|c| c.as_os_str() == "crates") {
        v[..v.len() - 3].iter().collect::<PathBuf>()
    } else {
        current_dir.clone()
    };

    base_dir.join(SQL_DIR)
}

async fn pexec(file: &Path, p_url: &str) -> Result<()> {
    info!("pexec: {file:?}");

    let cmd = "psql";
    let pa = file.to_str().unwrap();
    let args = &[p_url, "-f", pa]; // 替换为你想要执行的命令和参数

    let output = Command::new(cmd)
        .args(args)
        .output()
        .map_err(|_| Error::Sql)?;

    // let stdout = String::from_utf8_lossy(&output.stdout);
    let stderr = String::from_utf8_lossy(&output.stderr);

    // 你可以根据需要处理stdout和stderr
    if !stderr.is_empty() {
        error!("stderr: {}", stderr);
        return Err(Error::Sql);
    }

    Ok(())
}
